#include <arp.h>

// 定义只有本文件可以操作的接收缓冲区
char rcvbuf[RCVBUFSIZE];
/*
* 生成原始套接字
* timeout 读取超时时间
*/
int sockraw(int timeout) {
    int fd;
    if((fd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ARP))) < 0 ) {
        perror("socket");
        return ARP_ERROR;
    }
    if(!timeout) timeout = 10;
    struct timeval t = {timeout, 0};
    if( -1 == setsockopt(fd,SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t))) {
        perror("setsockopt");
        return ARP_ERROR;
    }
    return fd;
}

/*
* 检查是否支持发送Arp
*/
int check_flags(struct ifi_rinfo *ifr) {
    if ((ifr->flags & IFF_UP ) == 0) {
         printf("interface is not running\n");
         return -1;   
    }
    if ( ifr->flags & IFF_LOOPBACK ) {
        printf("interface is loopback\n");
        return -1;
    } 
    if ( ifr->flags & IFF_NOARP ) {
        printf("interface is noarp\n");
        return -1;
    }
    return 0;
}
/*
* 发送arp包
* sockfd 套接字句柄
* rinfo  接口的信息
* dstaddr 发送的目标ip地址
* addr   链路层socket_ll
*/
int arp_send(int sockfd, struct ifi_rinfo *rinfo, char *dstaddr, struct sockaddr_ll *addr) {
    struct arp_packet packet;
    struct sockaddr_in *sa;
    int rtval;
    memset(&packet, 0, sizeof(struct arp_packet));
    memset(packet.mac_target, 0xff, ETHER_ADDR_LEN);
    memcpy(packet.mac_source, rinfo->hwaddr, ETHER_ADDR_LEN);
    packet.ether_type = htons(ETH_P_ARP);
    packet.hw_type = htons(1);
    packet.proto_type = htons(ETH_P_IP);
    packet.mac_addr_len = 6;
    packet.ip_addr_len = 4;
    packet.opcode = htons(ARPOP_REQUEST) ;
    memcpy(packet.mac_sender, rinfo->hwaddr, ETHER_ADDR_LEN);

    sa = (struct sockaddr_in *)rinfo->ipaddr;
    memcpy(packet.ip_sender, &sa->sin_addr, 4);
    memset(packet.mac_receiver, 0, ETHER_ADDR_LEN);
    
    inet_pton(AF_INET, dstaddr, &sa->sin_addr);
    memcpy(packet.ip_receiver, &sa->sin_addr, 4);

    rtval = sendto(sockfd, &packet, sizeof(struct arp_packet), 0, 
            (struct sockaddr *)addr, sizeof(struct sockaddr_ll));    
    if (rtval < 0)  
    {  
        perror("sendto");
        return ARP_ERROR;  
    }  
    return ARP_OK;  
}
/*
* 接收arp响应
*/
void arp_recv(int sockfd, struct ifi_rinfo *rinfo, char *dstipaddr) {
    struct sockaddr_ll dstsock; 
    int n;
    char srcip[IP_ALEN];
    char dstip[IP_ALEN];
    socklen_t addrlen = sizeof(struct sockaddr_ll);
    while(1) {
        n = recvfrom(sockfd, rcvbuf, RCVBUFSIZE, 0, (struct sockaddr *)&dstsock, &addrlen);
        // 如果n为-1
        if (n == -1){
             // 如果是中断信号忽略
            if(errno == EINTR) {
                continue;
            } else if(errno == EAGAIN) {
                printf("Request timeout\n");
                return;
            } else {
                perror("recvfrom()");
                exit(ARP_ERROR);
            }
        }
        // 分析报文
        struct arp_packet *packet = (struct arp_packet *)rcvbuf;
        // 判断是否是arp响应报文
        if(packet->opcode != htons(ARPOP_REPLY)){
            continue;
        }
        if(packet->proto_type != htons(ETH_P_IP) || packet->ip_addr_len != 4) {
            continue;
        }
        // 判断来源是否为发送报文的目标地址
        
        // 输出
        inet_ntop(AF_INET, packet->ip_sender, srcip, sizeof(srcip)); // 发送者的IP
        inet_ntop(AF_INET, packet->ip_receiver, dstip, sizeof(dstip)); // 接收者的IP
        
        // 发送者IP与发送报文中的接收者IP不一致 或者 接收者IP与发送报文中的发送者不一致
        if((strcmp(srcip, dstipaddr) != 0) ||  (strcmp(dstip, rinfo->ipaddr_str) != 0)) {
            continue;
        }
        printf("%s reply from %s [%02X:%02X:%02X:%02X:%02X:%02X]\n",
            dstsock.sll_pkttype == PACKET_HOST ? "Unicast" : "Broadcast",
            srcip, 
            packet->mac_sender[0], 
            packet->mac_sender[1],
            packet->mac_sender[2],
            packet->mac_sender[3],
            packet->mac_sender[4],
            packet->mac_sender[5]
        );
        break;
    }
}